//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
//------------------------------------------------------------------------------
//
//  File:  menu.c
//
#include <eboot.h>
#include <pagemenu.h>
//------------------------------------------------------------------------------
//
//  Define:  dimof
//
#ifdef dimof
#undef dimof
#endif
#define dimof(x)                (sizeof(x)/sizeof(x[0]))

//------------------------------------------------------------------------------

static VOID ShowFlashGeometry(OAL_BLMENU_ITEM *pMenu);
static VOID EraseFlash(OAL_BLMENU_ITEM *pMenu);
static VOID EraseBlock(OAL_BLMENU_ITEM *pMenu);
static VOID SetBadBlock(OAL_BLMENU_ITEM *pMenu);
static VOID DumpFlash(OAL_BLMENU_ITEM *pMenu);
static UINT32 OALStringToUINT32(LPCWSTR psz);

//------------------------------------------------------------------------------

OAL_BLMENU_ITEM g_menuFlash[] = {
                                    {
                                        L'1', L"Show flash geometry", ShowFlashGeometry,
                                        NULL, NULL, NULL
                                    }, {
                                        L'2', L"Dump flash sector", DumpFlash,
                                        NULL, NULL, NULL
                                    }, {
                                        L'3', L"Erase flash", EraseFlash,
                                        NULL, NULL, NULL
                                    }, {
                                        L'4', L"Erase block range", EraseBlock,
                                        NULL, NULL, NULL
                                    }, {
                                        L'5', L"Set bad block", SetBadBlock,
                                        NULL, NULL, NULL
                                    },
                                    {
                                        L'0', L"Exit and Continue", NULL,
                                        NULL, NULL, NULL
                                    }, {
                                        0, NULL, NULL,
                                        NULL, NULL, NULL
                                    }
                                };


//------------------------------------------------------------------------------

VOID
ShowFlashGeometry(OAL_BLMENU_ITEM *pMenu)
{
	HANDLE hFMD;
	PCI_REG_INFO regInfo;
	FlashInfo flashInfo;
	LPCWSTR pszType;
	BLOCK_ID block;
	UINT32 status;
	UINT32 listmode=0;
#ifndef EBOOT_NO_LCD

	SHOW_SETTING_START();
#endif

	regInfo.MemBase.Reg[0] = S3C2443_BASE_REG_PA_NAND;
	hFMD = FMD_Init(NULL, &regInfo, NULL);
	if (hFMD == NULL)
	{
		OALLog(L" Oops, can't open FMD driver\r\n");
		goto cleanUp;
	}

	if (!FMD_GetInfo(&flashInfo))
	{
		OALLog(L" Oops, can't get flash geometry info\r\n");
		goto cleanUp;
	}

	switch (flashInfo.flashType)
	{
	case NAND:
		pszType = L"NAND";
		break;
	case NOR:
		pszType = L"NOR";
		break;
	default:
		pszType = L"Unknown";
	}

	printk(L"\r\n");
	printk(L" Flash Type:    %s\r\n", pszType);
	printk(L" Blocks:        %d\r\n", flashInfo.dwNumBlocks);
	printk(L" Bytes/block:   %d\r\n", flashInfo.dwBytesPerBlock);
	printk(L" Sectors/block: %d\r\n", flashInfo.wSectorsPerBlock);
	printk(L" Bytes/sector:  %d\r\n", flashInfo.wDataBytesPerSector);

	// now list bad/reserved sectors

	// First offset given
	block = 0;
	while (block < flashInfo.dwNumBlocks)
	{

		// If block is bad, we have to offset it
		status = FMD_GetBlockStatus(block);

		// bad block
		if ((status & BLOCK_STATUS_BAD) != 0)
		{
			if (listmode!=1)
			{
				printk(L"\r\n [bad]     ");
				listmode=1;
			}

			printk(L" %d", block);

			block++;
			continue;
		}

		// reserved block
		if ((status & BLOCK_STATUS_RESERVED) != 0)
		{
			if (listmode!=2)
			{
				printk(L"\r\n [reserved]");
				listmode=2;
			}

			printk(L" %d", block);

			block++;
			continue;
		}

		block++;
	}

	OALLog(L" Done\r\n");
#ifndef EBOOT_NO_LCD

	SHOW_SETTING_END();
#endif
cleanUp:
	if (hFMD != NULL)
	{
		FMD_Deinit(hFMD);
	}
	return;
}

//------------------------------------------------------------------------------

VOID EraseFlash(OAL_BLMENU_ITEM *pMenu)
{
	WCHAR key;
	HANDLE hFMD = NULL;
	PCI_REG_INFO regInfo;
	FlashInfo flashInfo;
	BLOCK_ID block;
	UINT32 status;


	OALLog(L" Do you want to erase unreserved blocks [-/y]? ");

	// Get key
	key = OALBLMenuReadKey(TRUE);
	OALLog(L"%c\r\n", key);

	// Depending on result
	if (key != L'y' && key != L'Y')
		goto cleanUp;

	// Open FMD
	regInfo.MemBase.Reg[0] = S3C2443_BASE_REG_PA_NAND;
	hFMD = FMD_Init(NULL, &regInfo, NULL);
	if (hFMD == NULL)
	{
		OALLog(L" Oops, can't open FMD driver\r\n");
		goto cleanUp;
	}

	if (!FMD_GetInfo(&flashInfo))
	{
		OALLog(L" Oops, can't get flash geometry info\r\n");
		goto cleanUp;
	}

	// First offset given
	block = 0;
	while (block < flashInfo.dwNumBlocks)
	{

		// If block is bad, we have to offset it
		status = FMD_GetBlockStatus(block);

		// Skip reserved blocks
		if ((status & BLOCK_STATUS_RESERVED) != 0)
		{
			OALLog(L" Skip reserved block %d\r\n", block);
			block++;
			continue;
		}

		// Skip bad blocks
		if ((status & BLOCK_STATUS_BAD) != 0)
		{
			OALLog(L" Skip bad block %d\r\n", block);
			block++;
			continue;
		}

		// Erase block
		if (!FMD_EraseBlock(block))
		{
			OALLog(L" Oops, can't erase block %d\r\n", block);
			break;
		}

		block++;
	}

	OALLog(L" Done\r\n");

cleanUp:
	if (hFMD != NULL)
		FMD_Deinit(hFMD);
	return;
}


//------------------------------------------------------------------------------

VOID EraseBlock(OAL_BLMENU_ITEM *pMenu)
{
	WCHAR key;
	HANDLE hFMD = NULL;
	PCI_REG_INFO regInfo;
	FlashInfo flashInfo;
	BLOCK_ID firstblock, lastblock=0;
	WCHAR szInputLine[16];
	UINT32 status;

	OALLog(L"\r\n First Block Number: ");

	if (OALBLMenuReadLine(szInputLine, dimof(szInputLine)) == 0)
	{
		goto cleanUp;
	}

	// Get block number
	firstblock = OALStringToUINT32(szInputLine);

	OALLog(L"\r\n Last Block Number: ");

	if (OALBLMenuReadLine(szInputLine, dimof(szInputLine)) != 0)
	{
		// Get block number
		lastblock = OALStringToUINT32(szInputLine);
	}

	if (lastblock < firstblock)
	{
		lastblock=firstblock;
	}

	// Open FMD
	regInfo.MemBase.Reg[0] = S3C2443_BASE_REG_PA_NAND;
	hFMD = FMD_Init(NULL, &regInfo, NULL);
	if (hFMD == NULL)
	{
		OALLog(L" Oops, can't open FMD driver\r\n");
		goto cleanUp;
	}

	if (!FMD_GetInfo(&flashInfo))
	{
		OALLog(L" Oops, can't get flash geometry info\r\n");
		goto cleanUp;
	}

	if (lastblock >= flashInfo.dwNumBlocks)
	{
		OALLog(L" Oops, too big block number\r\n");
		goto cleanUp;
	}

	OALLog(L" Do you want erase block %d-%d [-/y]? ", firstblock, lastblock);

	// Get key
	key = OALBLMenuReadKey(TRUE);
	OALLog(L"%c\r\n", key);

	// Depending on result
	if (key != L'y' && key != L'Y')
	{
		goto cleanUp;
	}

	while (firstblock<=lastblock)
	{

		// If block is bad, we have to offset it
		status = FMD_GetBlockStatus(firstblock);

		// ask before erasing reserved blocks
		if ((status & BLOCK_STATUS_RESERVED) != 0)
		{

			OALLog(L" Do you want to erase reserved block %d [-/y]? ", firstblock);

			// Get key
			key = OALBLMenuReadKey(TRUE);
			OALLog(L"%c\r\n", key);

			// Depending on result
			if (key != L'y' && key != L'Y')
			{
				firstblock++;
				continue;
			}

		}

		// ask before erasing bad blocks
		if ((status & BLOCK_STATUS_BAD) != 0)
		{

			OALLog(L" Do you want to erase bad block %d [-/y]? ", firstblock);

			// Get key
			key = OALBLMenuReadKey(TRUE);
			OALLog(L"%c\r\n", key);

			// Depending on result
			if (key != L'y' && key != L'Y')
			{
				firstblock++;
				continue;
			}

		}

		// Erase block
		if (!FMD_EraseBlock(firstblock))
		{
			OALLog(L" Oops, can't erase block %d - mark as bad\r\n", firstblock);
			FMD_SetBlockStatus(firstblock, BLOCK_STATUS_BAD);
		}

		firstblock++;
		OALLog(L".");
	}

	OALLog(L" Done\r\n");

cleanUp:
	if (hFMD != NULL)
		FMD_Deinit(hFMD);
	return;
}

//------------------------------------------------------------------------------

VOID DumpFlash(OAL_BLMENU_ITEM *pMenu)
{
	HANDLE hFMD = NULL;
	PCI_REG_INFO regInfo;
	FlashInfo flashInfo;
	SectorInfo sectorInfo;
	SECTOR_ADDR sector;
	WCHAR szInputLine[16];
	UINT8 buffer[2048];
	UINT32 i, j;


	// Open FMD
	regInfo.MemBase.Reg[0] = S3C2443_BASE_REG_PA_NAND;
	hFMD = FMD_Init(NULL, &regInfo, NULL);
	if (hFMD == NULL)
	{
		OALLog(L" Oops, can't open FMD driver\r\n");
		goto cleanUp;
	}

	if (!FMD_GetInfo(&flashInfo))
	{
		OALLog(L" Oops, can't get flash geometry info\r\n");
		goto cleanUp;
	}

	if (flashInfo.wDataBytesPerSector > sizeof(buffer))
	{
		OALLog(L" Oops, sector size larger than my buffer\r\n");
		goto cleanUp;
	}

	while (TRUE)
	{

		OALLog(L"\r\n Sector Number: ");

		if (OALBLMenuReadLine(szInputLine, dimof(szInputLine)) == 0)
		{
			break;
		}

		// Get sector number
		sector = OALStringToUINT32(szInputLine);

		// Check sector number
		if (sector > flashInfo.dwNumBlocks * flashInfo.wSectorsPerBlock)
		{
			OALLog(L" Oops, too big sector number\r\n");
			continue;
		}

		if (!FMD_ReadSector(sector, buffer, &sectorInfo, 1))
		{
			OALLog(L" Oops, sector read failed\r\n");
			continue;
		}

		OALLog(
		    L"\r\nSector %d (sector %d in block %d)\r\n", sector,
		    sector%flashInfo.wSectorsPerBlock, sector/flashInfo.wSectorsPerBlock
		);
		OALLog(
		    L"Reserved1: %08x OEMReserved: %02x Bad: %02x Reserved2: %04x\r\n",
		    sectorInfo.dwReserved1, sectorInfo.bOEMReserved,
		    sectorInfo.bBadBlock, sectorInfo.wReserved2
		);

		for (i = 0; i < flashInfo.wDataBytesPerSector; i += 16)
		{
			OALLog(L"%04x ", i);
			for (j = i; j < i + 16 && j < flashInfo.wDataBytesPerSector; j++)
			{
				OALLog(L" %02x", buffer[j]);
			}
			OALLog(L"  ");
			for (j = i; j < i + 16 && j < flashInfo.wDataBytesPerSector; j++)
			{
				if (buffer[j] >= ' ' && buffer[j] < 127)
				{
					OALLog(L"%c", buffer[j]);
				}
				else
				{
					OALLog(L".");
				}
			}
			OALLog(L"\r\n");
		}

	}

cleanUp:

	if (hFMD != NULL)
	{
		FMD_Deinit(hFMD);
	}

	return;
}

//------------------------------------------------------------------------------

VOID SetBadBlock(OAL_BLMENU_ITEM *pMenu)
{
	HANDLE hFMD = NULL;
	PCI_REG_INFO regInfo;
	FlashInfo flashInfo;
	BLOCK_ID blockId;
	WCHAR szInputLine[16];


	// Open FMD
	regInfo.MemBase.Reg[0] = S3C2443_BASE_REG_PA_NAND;
	hFMD = FMD_Init(NULL, &regInfo, NULL);
	if (hFMD == NULL)
	{
		OALLog(L" Oops, can't open FMD driver\r\n");
		goto cleanUp;
	}

	if (!FMD_GetInfo(&flashInfo))
	{
		OALLog(L" Oops, can't get flash geometry info\r\n");
		goto cleanUp;
	}

	OALLog(L"\r\n Block Number: ");

	if (OALBLMenuReadLine(szInputLine, dimof(szInputLine)) == 0)
	{
		goto cleanUp;
	}

	// Get sector number
	blockId = OALStringToUINT32(szInputLine);

	// Check sector number
	if (blockId >= flashInfo.dwNumBlocks)
	{
		OALLog(L" Oops, too big block number\r\n");
		goto cleanUp;
	}

	FMD_SetBlockStatus(blockId, BLOCK_STATUS_BAD);

	OALLog(L"\r\n Done\r\n");

cleanUp:
	if (hFMD != NULL)
	{
		FMD_Deinit(hFMD);
	}

	return;
}

//------------------------------------------------------------------------------

UINT32 OALStringToUINT32(LPCWSTR psz)
{
	UINT32 i = 0;

	// Replace the dots with NULL terminators
	while (psz != NULL && *psz != L'\0')
	{
		if (*psz < L'0' || *psz > L'9')
		{
			break;
		}
		i = i * 10 + (*psz - L'0');
		psz++;
	}

	return i;
}

//------------------------------------------------------------------------------
